package com.tiger.utils;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tiger.mapper.homePage.HotArticleMapper;
import com.tiger.pojo.homePage.HotArticle;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author HuXuehao (StudiousTiger)
 * @desc 这个类的作用是 同步mysql与elasticsearch之间的记录
 * @date 2022/6/14
 * 注意： 前提是先进行mysql的插入、更新、删除操作
 */
@Component
public class Sync2ElasticsearchUtils {
    @Autowired
    private HotArticleMapper hotArticleMapper;

    @Qualifier("restHighLevelClient")
    @Autowired
    private RestHighLevelClient client;

    private static final String  INDEX_NAME = "homepage_of_search";
    /**
     * 更新操作引起的同步
     * @param hotArticleID 被同步作品
     */
    @Async  // 异步任务
    public void updateOpt(String hotArticleID) {
        try {
            HotArticle hotArticle = getHotArticleById(hotArticleID);
            boolean res = updateSync4ES(hotArticle);
            System.out.println("update-sync=======>>"+res);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 同步error
    private boolean updateSync4ES(HotArticle hotArticle) throws Exception{
        String hotArticleId;
        if (hotArticle != null) {
            hotArticleId = hotArticle.getArticleId();
        } else {
            return false;
        }
        try {
            if (isExistIndex(INDEX_NAME) && isExistContext(INDEX_NAME, hotArticleId)) {
                UpdateRequest updateRequest = new UpdateRequest(INDEX_NAME, hotArticleId);
                updateRequest.timeout(TimeValue.timeValueSeconds(3)); // 超时时间
                // 将error转成json，并传入请求
                updateRequest.doc(JSON.toJSONString(hotArticle), XContentType.JSON);
                // 客户端执行更新操作
                client.update(updateRequest, RequestOptions.DEFAULT);
            } else {
                publishSync4ES(hotArticle);
            }
        } catch (Exception e) {
            return false;
        }

        return true;
    }

    /**
     * 发布操作引起的同步
     * @param hotArticleID
     */
    @Async  // 异步任务
    public void publishOpt(String hotArticleID) {
        try {
            HotArticle hotArticle = getHotArticleById(hotArticleID);
            boolean res = publishSync4ES(hotArticle);
            System.out.println("publish-sync=======>>"+res);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 同步es
    private boolean publishSync4ES(HotArticle hotArticle) throws Exception{
        String hotArticleId;
        if (hotArticle != null) {
            hotArticleId = hotArticle.getArticleId();
        } else {
            return false;
        }
        try {
            ifNotExistIndexCreateIndex(INDEX_NAME); // 索引不存在则创建
            IndexRequest indexRequest = new IndexRequest(INDEX_NAME);
            indexRequest.id(hotArticleId); // 设置ID
            indexRequest.timeout(TimeValue.timeValueSeconds(3)); // 设置超时
            indexRequest.source(JSON.toJSONString(hotArticle),XContentType.JSON); // 设置请求体
            client.index(indexRequest, RequestOptions.DEFAULT); // 执行插入
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    // 根据Id获取Article
    private HotArticle getHotArticleById(String id) {
        QueryWrapper<HotArticle> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("article_id", id);
        return hotArticleMapper.selectOne(queryWrapper);
    }

    /**
     * 发布操作引起的同步
     * @param id
     */
    @Async  // 异步任务
    public void deleteOpt(String id) throws Exception {
        try {
            if (isExistIndex(INDEX_NAME) && isExistContext(INDEX_NAME, id)) {
                DeleteRequest request = new DeleteRequest(INDEX_NAME, id);
                request.timeout(TimeValue.timeValueSeconds(3));
                DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);
                System.out.println("delete-sync=======>>"+delete.status());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 定时同步
     */
    @Async
    public void timingSync() {
        try {
            ifNotExistIndexCreateIndex(INDEX_NAME);
            // 1. 查询出es中所有的记录，存入map
            Map<String, HotArticle> map4Es = new HashMap<>(); // 存储es中的记录
            SearchRequest searchRequest = new SearchRequest(INDEX_NAME); // 查询请求
            searchRequest.source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery())); // 创建查询builder
            SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT); // 执行
            SearchHits hits = search.getHits();  // 搜索命中数
            for (SearchHit hit : hits) {
                // 将字符串转化成对象
                HotArticle hotArticle = JSON.parseObject(hit.getSourceAsString(), HotArticle.class);
                map4Es.put(hotArticle.getArticleId(), hotArticle);
            }
            // 2. 查询出中所有的hot文章,存入map
            List<HotArticle> hotArticles4Sql = hotArticleMapper.selectList(null);
            Map<String, HotArticle> map4Sql = new HashMap<>(); // 存储es中的记录
            for (HotArticle hotArticle : hotArticles4Sql) {
                map4Sql.put(hotArticle.getArticleId(), hotArticle);
            }

            for (HotArticle hotArticle : hotArticles4Sql) {
                if (map4Es.get(hotArticle.getArticleId()) == null) {
                    // 3. es中没有的hot有的进行插入
                    push2Es(hotArticle);
                } else if (JSON.toJSONString(map4Es.get(hotArticle.getArticleId()))
                        .equals(map4Sql.get(hotArticle.getArticleId()))){
                    // 4. 找出es中与hot中的区别，进行更新
                    update2Es(hotArticle);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 根据ID对es中指定索引的记录（文档）进行更细
    private void update2Es(HotArticle hotArticle) throws Exception {
        if (isExistIndex(INDEX_NAME) && isExistContext(INDEX_NAME, hotArticle.getArticleId())) {
            UpdateRequest updateRequest = new UpdateRequest(INDEX_NAME, hotArticle.getArticleId());
            updateRequest.timeout(TimeValue.timeValueSeconds(3)); // 超时时间
            // 将error转成json，并传入请求
            updateRequest.doc(JSON.toJSONString(hotArticle), XContentType.JSON);
            // 客户端执行更新操作
            client.update(updateRequest, RequestOptions.DEFAULT);
        } else {
            push2Es(hotArticle);
        }
    }
    // 直接将hotArticle添加到es中对应的索引中
    private void push2Es(HotArticle hotArticle) throws Exception {
        try {
            ifNotExistIndexCreateIndex(INDEX_NAME); // 索引不存在则创建
            IndexRequest indexRequest = new IndexRequest(INDEX_NAME);
            indexRequest.id(hotArticle.getArticleId()); // 设置ID
            indexRequest.timeout(TimeValue.timeValueSeconds(3)); // 设置超时
            indexRequest.source(JSON.toJSONString(hotArticle),XContentType.JSON); // 设置请求体
            client.index(indexRequest, RequestOptions.DEFAULT); // 执行插入
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 判断index是否存在，不存在则创建
     * @param indexName 索引名
     * @throws Exception
     */
    private void ifNotExistIndexCreateIndex (String indexName) throws Exception {
        // 判断索引是否存在
        GetIndexRequest getIndexRequest = new GetIndexRequest(indexName);
        boolean exists = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
        if (exists == false) {
            //创建索引请求
            CreateIndexRequest req = new CreateIndexRequest(indexName);
            //客户端执行请求
            client.indices().create(req, RequestOptions.DEFAULT);
        }
    }

    /**
     * 判断index是否存在
     * @param indexName 索引名
     * @return
     * @throws Exception
     */
    private boolean isExistIndex (String indexName) throws Exception {
        // 判断索引是否存在
        GetIndexRequest getIndexRequest = new GetIndexRequest(indexName);
        return client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
    }

    /**
     * 判断文档记录是否存在
     * @param indexName 索引名
     * @param id 文档记录id
     * @return
     * @throws IOException
     */
    private boolean isExistContext(String indexName, String id) throws IOException {
        try {
            // 创建一个请求
            GetRequest getRequest = new GetRequest(indexName, id);
            // 不获取上下文，提高判断的效率
            getRequest.fetchSourceContext(new FetchSourceContext(false));
            getRequest.storedFields("_none_");
            // 判断是否存在
            return client.exists(getRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
}
